package io.github.wanggit.antrpc.boot;

import io.github.wanggit.antrpc.client.zk.lb.ILoadBalancer;
import io.github.wanggit.antrpc.client.zk.zknode.DirectNodeHostEntity;
import io.github.wanggit.antrpc.commons.bean.Host;
import io.github.wanggit.antrpc.commons.config.CircuitBreakerConfig;
import io.github.wanggit.antrpc.commons.config.IConfiguration;
import io.github.wanggit.antrpc.commons.constants.ConstantValues;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * AntRpc的配置类
 */
@Configuration
@EnableConfigurationProperties(RpcProperties.class)
public class AntRpcAutoConfiguration {

    private static final String INTERFACE_BREAKER = "interface-circuit-breaker";
    private final ConfigurationPropertyName RPC_INTERFACE_PROP =
            ConfigurationPropertyName.of(
                    ConstantValues.ANTRPC_CONFIG_PREFIX + "." + INTERFACE_BREAKER);
    private final Bindable<Map<String, String>> RPC_INTERFACE_MAP =
            Bindable.mapOf(String.class, String.class);

    @Bean
    public AntrpcStater antrpcStater(ApplicationContext applicationContext) {
        return new AntrpcStater(applicationContext);
    }

    /**
     * 解析得到AntRpc的配置对象
     *
     * @param environment spring environment
     * @return configuration
     */
    @Bean
    public IConfiguration configuration(Environment environment) {
        io.github.wanggit.antrpc.commons.config.Configuration configuration =
                new io.github.wanggit.antrpc.commons.config.Configuration();
        Binder binder = Binder.get(environment);
        RpcProperties rpcProperties = getRpcProperties(binder);
        initConfiguration(configuration, rpcProperties);
        // 解析出接口级别的熔断器配置
        Map<String, String> map = getCircuitBreakerConfigMap(binder);
        Map<String, CircuitBreakerConfig> interfaceBreakers = new HashMap<>(map.size() * 2);
        map.forEach(
                (key, value) -> {
                    String interfaceName = key.substring(0, key.lastIndexOf('.'));
                    if (!interfaceBreakers.containsKey(interfaceName)) {
                        interfaceBreakers.put(interfaceName, new CircuitBreakerConfig());
                    }
                    CircuitBreakerConfig config = interfaceBreakers.get(interfaceName);
                    switch (key) {
                        case "enabled":
                            config.setEnabled(Boolean.parseBoolean(value));
                            break;
                        case "failureRateThreshold":
                        case "failure-rate-threshold":
                            config.setFailureRateThreshold(Integer.parseInt(value));
                            break;
                        case "permittedNumOfCallsInHalfOpen":
                        case "permitted-num-of-calls-in-half-open":
                            config.setPermittedNumOfCallsInHalfOpen(Integer.parseInt(value));
                            break;
                    }
                });
        interfaceBreakers.forEach((key, value) -> value.checkSelf());
        configuration.setInterfaceBreakerConfigs(interfaceBreakers);
        configuration.setEnvironment(environment);
        return configuration;
    }

    /**
     * 能Spring Boot配置中获配置信息，此时只包含 RpcProperties中能自动解析的配置，不包含
     * io.github.wanggit.antrpc.boot.RpcProperties#interfaceCircuitBreaker 属性
     *
     * @param binder binder
     * @return rpc properties
     */
    private RpcProperties getRpcProperties(Binder binder) {
        return binder.bind(
                        ConfigurationPropertyName.of(ConstantValues.ANTRPC_CONFIG_PREFIX),
                        Bindable.of(RpcProperties.class))
                .orElseGet(RpcProperties::new);
    }

    /**
     * 把RpcProperties中的属性都添加到 configuration中，在无数据时添加默认值
     *
     * @param configuration confiuration
     * @param rpcProperties properties
     */
    private void initConfiguration(
            io.github.wanggit.antrpc.commons.config.Configuration configuration,
            RpcProperties rpcProperties) {
        configuration.setZkIps(
                null == rpcProperties.getZkServers()
                        ? ConstantValues.RPC_DEFAULT_ZK_SERVER
                        : rpcProperties.getZkServers());
        configuration.setPort(
                null == rpcProperties.getPort()
                        ? ConstantValues.RPC_DEFAULT_PORT
                        : rpcProperties.getPort());
        configuration.setExposeIp(rpcProperties.getExposedIp());
        // 负载均衡器
        String loadBalancer = rpcProperties.getLoadBalancer();
        if (null != loadBalancer) {
            try {
                //noinspection unchecked
                configuration.setLoadBalancerName(
                        (Class<? extends ILoadBalancer>) Class.forName(loadBalancer));
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
        }
        // 开发环境下的服务直连
        if (null != rpcProperties.getDirectHosts() && !rpcProperties.getDirectHosts().isEmpty()) {
            Map<String, DirectNodeHostEntity> directHostEntities =
                    new HashMap<>(rpcProperties.getDirectHosts().size() * 2);
            rpcProperties
                    .getDirectHosts()
                    .forEach(
                            (key, value) -> {
                                DirectNodeHostEntity directNodeHostEntity =
                                        DirectNodeHostEntity.from(Host.parse(value));
                                directHostEntities.put(key, directNodeHostEntity);
                            });
            configuration.setDirectHosts(directHostEntities);
        }
        configuration.setStartServer(rpcProperties.isStartServer());
        configuration.setGlobalBreakerConfig(rpcProperties.getCircuitBreakers());
        configuration.setConnectionBreakerConfig(rpcProperties.getConnectionBreakerConfig());
        configuration.setCallLogReporterConfig(rpcProperties.getCallLogReporterConfig());
        configuration.setRpcClientsConfig(rpcProperties.getRpcClientsConfig());
        configuration.setCodecConfig(rpcProperties.getCodecConfig());
        configuration.setSerializeConfig(rpcProperties.getSerializeConfig());
        configuration.setTelnetConfig(rpcProperties.getTelnetConfig());
    }

    /**
     * 获取接口级别设置的熔断器
     *
     * @param binder binder
     * @return map
     */
    private Map<String, String> getCircuitBreakerConfigMap(Binder binder) {
        return binder.bind(RPC_INTERFACE_PROP, RPC_INTERFACE_MAP).orElseGet(Collections::emptyMap);
    }
}
